package org.msb.accentis.gui.models;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import javax.swing.AbstractListModel;

import org.msb.accentis.data.DataManagerFactory;
import org.msb.accentis.data.IDataManager;
import org.msb.accentis.data.IDataManagerFactoryListener;
import org.msb.accentis.data.IDataManagerListener;
import org.msb.accentis.data.pojo.NamedObject;

public class PojoListModel<T extends NamedObject> extends AbstractListModel<T> implements IDataManagerFactoryListener {

	private final String pojoQueryName;
	
	private Map<String, Object> pojoQueryParameters;
	
	private final Class<T> pojoClass;
	
	private List<T> list = new ArrayList<T>();
	
	private boolean includeNullInList;
	
	public PojoListModel(String queryName, Class<T> klass) {
		this(queryName, klass, false);
	}
	
	public PojoListModel(String queryName, Class<T> klass, boolean includeNull) {
		DataManagerFactory.getInstance().addDataManagerFactoryListener(this);
		pojoQueryName = queryName;
		pojoClass = klass;
		includeNullInList = includeNull;
	}

	public void reload(Map<String, Object> queryParameters) {
		clear();
		pojoQueryParameters = queryParameters;
		load(DataManagerFactory.getInstance().getDataManager());
	}
	
	@Override
	public int getSize() {
		return list.size() + (includeNullInList ? 1 : 0);
	}

	@Override
	public T getElementAt(int index) {
		if (includeNullInList && 0 == index)
			return null;
		
		return list.get(index - (includeNullInList ? 1 : 0));
	}

	@Override
	public void dataManagerChanged(IDataManager dataManager) {
		reload(getPojoQueryParameters());
	}
	
	private void clear() {
		int size = getSize();
		list.clear();
		fireIntervalRemoved(this, (includeNullInList ? 1 : 0), size);
	}

	private void load(IDataManager dataManager) {
		if (null != dataManager && null != getPojoQueryParameters()) {
			list.addAll(dataManager.<T>runNamedQuery(pojoQueryName, getPojoQueryParameters()));
			dataManager.addDataManagerListener(createDataManagerListener(), pojoClass);
			fireIntervalAdded(this, (includeNullInList ? 1 : 0), getSize());
		}
	}
	
	protected Map<String, Object> getPojoQueryParameters() {
		return pojoQueryParameters;
	}
	
	private IDataManagerListener<T> createDataManagerListener() {
		return new IDataManagerListener<T>() {
			@Override
			public void objectCreated(T object) {
				int index = Collections.binarySearch(list, object);
				if (0 <= index)
					return;
				
				index = -index - 1;
				
				if (includeNullInList)
					++index;
				
				list.add(index, object);
				fireIntervalAdded(this, index, index);
			}

			@Override
			public void objectRemoved(T object) {
				int index = Collections.binarySearch(list, object);
				if (0 > index)
					return;
				
				list.remove(index);
				
				if (includeNullInList)
					++index;
				
				fireIntervalRemoved(this, index, index);
			}

			@Override
			public void objectSaved(T object) {
				int preSortIndex = list.indexOf(object);
				Collections.sort(list);
				int postSortIndex = Collections.binarySearch(list, object);

				if (preSortIndex != postSortIndex) {
					if (includeNullInList) {
						++preSortIndex;
						++postSortIndex;
					}
					
					fireIntervalRemoved(this, preSortIndex, preSortIndex);

					if (postSortIndex > preSortIndex)
						postSortIndex--; // Account for the smaller row index having been removed.

					fireIntervalAdded(this, postSortIndex, postSortIndex);
				} else
					fireContentsChanged(this, preSortIndex, preSortIndex);
			}

			@Override
			public void objectRefreshed(T object) {
			}
		};
	}
}
